/******************************************************************************
 * Copyright (C), 2000-2014,  Frank.
 * filename: blast.c
 * Author: Frank
 * Version:
 * Data: 2014-12-9 
 * brief:
 ******************************************************************************/
#include "blast.h"

#define BLAST_RING_LEN	128

#define BIT0			0
#define BIT1			1
#define BIT_REPEAT	2
#define BIT_UNKNOW	3

#define PLUS		1
#define SPACE	0

#define DECODE_NONE	0
#define DECODE_DONE	1
#define DECODE_NEC_DOING	2
#define DECODE_RC6_SB		3
#define DECODE_RC6_MB		4
#define DECODE_RC6_TR		5
#define DECODE_RC6_DATA	6


#define INRANG(_i, _t, _r)	(((_i <= _t + _r) && (_i >= _t - _r))? TRUE:FALSE)
#define GET_PLUS(_x)	{_x = au32BlastPlus[u16Read]; if(++u16Read>BLAST_RING_LEN)u16Read=0;}

U16 u16Read = 0;
U16 u16Write = 0;
U32 au32BlastPlus[BLAST_RING_LEN];

static U8 u8DecodeState;
static U8 u8Protocol;
static U8 u8BitCnt;
static U32 u32LastCode;

static U8 u8RC6PlusState;

static inline U16 _blast_rem(void)
{
	if(u16Write > u16Read)
	{
		return u16Write-u16Read;
	}
	else if(u16Write < u16Read)
	{
		return BLAST_RING_LEN-(u16Read-u16Write);
	}
	else
	{
		return 0;
	}
}

static U32 _blast_dec_bit(U32 u32Plus, U32 u32Space)
{
	if(TRUE == INRANG(u32Plus, NEC_BIT0_PLUS, NEC_TOLERRANCE) &&
		TRUE == INRANG(u32Space, NEC_BIT0_SPACE, NEC_TOLERRANCE))
	{
		return BIT0;
	}

	if(TRUE == INRANG(u32Plus, NEC_BIT1_PLUS, NEC_TOLERRANCE) &&
		TRUE == INRANG(u32Space, NEC_BIT1_SPACE, NEC_TOLERRANCE))
	{
		return BIT1;
	}
	
	return BIT_UNKNOW;
}

static U32 _blast_rc6_bit(U32 u32Plus, U32 *pu32State, U8 *pu8Plus, U32 u32Curbit, U8 *pu8Jump)
{
	U32 u32Bit = BIT_UNKNOW;
	*pu8Jump = FALSE;
	
	switch(*pu32State)
	{
		case DECODE_RC6_SB:
			//Get start bit, start bit must 1,so,the bit is P-S
			if(TRUE == INRANG(u32Plus, RC6_BITPHASE, RC6_TOLERRANCE) )
			{
				*pu8Plus = SPACE;
				*pu32State = DECODE_RC6_MB;
				*pu8Jump = FALSE;
				u32Bit =  BIT1;
			}
			break;
		case DECODE_RC6_MB:
			if(u32Curbit>0 && u32Curbit<4)
			{
				if(*pu8Plus == PLUS)
				{
					//S-P to S-P, one phase,jump next phase, can't change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE, RC6_TOLERRANCE) )
					{
						*pu8Jump = TRUE;
						u32Bit =  BIT0;
					}

					//S-P to P-S, two phase, change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*2, RC6_TOLERRANCE*2) )
					{
						*pu8Plus = SPACE;
						*pu8Jump = FALSE;
						u32Bit =  BIT1;
					}
				}
				else
				{
					//P-S to P-S, one phase, jump next phase, can't change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE, RC6_TOLERRANCE) )
					{
						*pu8Jump = TRUE;
						u32Bit =  BIT1;
					}

					//P-S to S-P, two phase, change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*2, RC6_TOLERRANCE*2))
					{
						*pu8Plus = PLUS;
						*pu8Jump = FALSE;
						u32Bit =  BIT0;
					}
				}
				if(u32Curbit == 3 && u32Bit != BIT_UNKNOW)
				{
					*pu32State = DECODE_RC6_TR;
				}
			}
			break;
		case DECODE_RC6_TR:
			{
				if(*pu8Plus == PLUS)
				{
					//S-P to 2S-2P, one phase,jump next phase, can't change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE, RC6_TOLERRANCE))
					{
						*pu8Jump = TRUE;
						u32Bit =  BIT0;
					}

					//S-P to 2P-2S, three phase, change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*3, RC6_TOLERRANCE*2))
					{
						*pu8Plus = SPACE;
						*pu8Jump = FALSE;
						u32Bit =  BIT1;
					}
				}
				else
				{
					//P-S to 2P-2S, one phase, jump next phase, can't change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE, RC6_TOLERRANCE))
					{
						*pu8Jump = TRUE;
						u32Bit =  BIT1;
					}

					//P-S to 2S-2P, two phase, change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*3, RC6_TOLERRANCE*2))
					{
						*pu8Plus = PLUS;;
						*pu8Jump = FALSE;
						u32Bit =  BIT0;
					}
				}

				if(u32Bit != BIT_UNKNOW)
				{
					*pu32State = DECODE_RC6_DATA;
				}
				
			}
			break;
		case DECODE_RC6_DATA:
			if(u32Curbit == 5)
			{
				if(*pu8Plus == PLUS)
				{
					// 2S-2P to S-P, two phase,jump next phase, can't change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*2, RC6_TOLERRANCE))
					{
						*pu8Jump = TRUE;
						u32Bit =  BIT0;
					}

					// 2S-2P to P-S, three phase, change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*3, RC6_TOLERRANCE*2))
					{
						*pu8Plus = SPACE;
						*pu8Jump = FALSE;
						u32Bit =  BIT1;
					}
				}
				else
				{
					// 2P-2S to P-S, two phase, jump next phase, can't change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*2, RC6_TOLERRANCE))
					{
						*pu8Jump = TRUE;
						u32Bit =  BIT1;
					}

					//P-S to 2S-2P, three phase, change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*3, RC6_TOLERRANCE*2))
					{
						*pu8Plus = PLUS;;
						*pu8Jump = FALSE;
						u32Bit =  BIT0;
					}
				}
			}
			else
			{
				if(*pu8Plus == PLUS)
				{
					//S-P to S-P, one phase,jump next phase, can't change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE, RC6_TOLERRANCE))
					{
						*pu8Jump = TRUE;
						u32Bit =  BIT0;
					}

					//S-P to P-S, two phase, change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*2, RC6_TOLERRANCE*2))
					{
						*pu8Plus = SPACE;
						*pu8Jump = FALSE;
						u32Bit =  BIT1;
					}
				}
				else
				{
					//P-S to P-S, one phase, jump next phase, can't change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE, RC6_TOLERRANCE))
					{
						*pu8Jump = TRUE;
						u32Bit =  BIT1;
					}

					//P-S to S-P, two phase, change plus state				
					if(TRUE == INRANG(u32Plus, RC6_BITPHASE*2, RC6_TOLERRANCE*2))
					{
						*pu8Plus = PLUS;
						*pu8Jump = FALSE;
						u32Bit =  BIT0;
					}
				}
			}
			break;
		default:
			break;
	}

	return u32Bit;
}

void Blast_Init(void)
{
	u16Read = 0;
	u16Write = 0;
	u8BitCnt = 0;
	u8DecodeState = DECODE_NONE;
	u8Protocol = BLAST_PROTOCOL_UNKNOW;
}

void Blast_WritePlus(U32 u32Plus)
{
	if(_blast_rem()==BLAST_RING_LEN)
	{
		//Overflow
		return;
	}

	au32BlastPlus[u16Write] = u32Plus;
	if(++u16Write >= BLAST_RING_LEN)
	{
		u16Write = 0;
	}
}

BOOL Blast_ReadPlus(U32 *pu32Plus)
{
	if(_blast_rem()==0)
	{
		//Overflow
		return FALSE;
	}

	*pu32Plus = au32BlastPlus[u16Read];
	if(++u16Read >= BLAST_RING_LEN)
	{
		u16Read = 0;
	}

	return TRUE;
}

BOOL Blast_DoDecode(U8 *pu8Protocol, U32 *pu32Code)
{
	//find header
	U32 u32Plus, u32Space;
	U32 u32Bit;

	if(u8DecodeState == DECODE_NONE || u8DecodeState == DECODE_DONE)
	{
		while(_blast_rem()>2)	//Check header/repeat to recognition protocol
		{
			GET_PLUS(u32Plus);
			if(TRUE == INRANG(u32Plus, NEC_HEADER_PLUS, NEC_TOLERRANCE)) 		//NEC
			{
				GET_PLUS(u32Plus);
				if(TRUE == INRANG(u32Plus, NEC_HEADER_SPACE, NEC_TOLERRANCE))
				{
					u8Protocol = BLAST_PROTOCOL_NEC;
					u8BitCnt = 0;
					u8DecodeState = DECODE_NEC_DOING;
					break;
				}
				else if(TRUE == INRANG(u32Plus, NEC_REPEAT_SPACE, NEC_TOLERRANCE) && u8Protocol == BLAST_PROTOCOL_NEC && u8BitCnt == 32)
				{
					GET_PLUS(u32Plus);
					if(TRUE == INRANG(u32Plus, NEC_REPEAT_PLUS, NEC_TOLERRANCE))
					{
						*pu8Protocol = u8Protocol;
						*pu32Code = u32LastCode;
						return TRUE;
					}
				}
				else
				{
					u8DecodeState = DECODE_NONE;
				}				
			}
			else if(TRUE == INRANG(u32Plus, RC6_HEADER_PLUS, RC6_TOLERRANCE))		//RC6
			{
				GET_PLUS(u32Plus);
				if(TRUE == INRANG(u32Plus, RC6_HEADER_SPACE, RC6_TOLERRANCE))
				{
					u8Protocol = BLAST_PROTOCOL_RC6;
					u8BitCnt = 0;
					u8DecodeState = DECODE_RC6_SB;
					u8RC6PlusState = 0;
					break;
				}
				else
				{
					u8DecodeState = DECODE_NONE;
				}
			}
		}
		
	}

	if(u8DecodeState > DECODE_DONE)
	{
		switch(u8Protocol)
		{
			case BLAST_PROTOCOL_NEC:
				while(_blast_rem()>=2)
				{
					GET_PLUS(u32Plus);
					GET_PLUS(u32Space);
					u32Bit = _blast_dec_bit(u32Plus, u32Space);					
					switch(u32Bit)
					{
						case BIT0:
						case BIT1:
							*pu32Code = (*pu32Code<<u8BitCnt)|u32Bit;
							if(++u8BitCnt >= NEC_CODE_LEN)
							{
								u8DecodeState = DECODE_DONE;
								u32LastCode = *pu32Code;
								return TRUE;
							}
							break;
						case BIT_UNKNOW:
							*pu8Protocol = BLAST_PROTOCOL_UNKNOW;
							u8BitCnt = 0;
							u8DecodeState = DECODE_NONE;
							u8Protocol = BLAST_PROTOCOL_UNKNOW;
							return FALSE;
							break;
					}
				}
				break;

			case BLAST_PROTOCOL_RC6:
				while(_blast_rem()>=1)
				{
					U8 u8Jump = 0;
					GET_PLUS(u32Plus);
					u32Bit = _blast_rc6_bit(u32Plus, &u8DecodeState, &u8RC6PlusState, u8BitCnt, &u8Jump);	
					switch(u32Bit)
					{
						case BIT0:
						case BIT1:
							*pu32Code = (*pu32Code<<u8BitCnt)|u32Bit;
							if(++u8BitCnt >= RC6_CODE_LEN)
							{
								u8DecodeState = DECODE_DONE;
								u32LastCode = *pu32Code;
								return TRUE;
							}

							if(u8Jump != 0)
							{
								if(_blast_rem() == 0)
								{
									*pu8Protocol = BLAST_PROTOCOL_UNKNOW;
									u8BitCnt = 0;
									u8DecodeState = DECODE_NONE;
									u8Protocol = BLAST_PROTOCOL_UNKNOW;
									return FALSE;
								}
								GET_PLUS(u32Plus);
							}
							break;
						case BIT_UNKNOW:
							*pu8Protocol = BLAST_PROTOCOL_UNKNOW;
							u8BitCnt = 0;
							u8DecodeState = DECODE_NONE;
							u8Protocol = BLAST_PROTOCOL_UNKNOW;
							return FALSE;
							break;
					}
				}
				break;
			default:
				break;
		}
				
	}

	return FALSE;
}

void Blast_ExitDecode(void)
{
	u8BitCnt = 0;
	u8DecodeState = DECODE_NONE;
	u8Protocol = BLAST_PROTOCOL_UNKNOW;
}

U16 Blast_GetPlusNum(void)
{
	return _blast_rem();
}

